package service

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"sync"
	"time"

	v1 "MdfServer/api/room/v1"
	"MdfServer/internal/room/biz"

	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/transport/grpc"
)

const (
	roomMaxPlayers = 2       // 最大玩家数
	roomTimeout    = 10 * 60 // 房间超时时间（秒）
)

// RoomService 房间服务实现
type RoomService struct {
	v1.UnimplementedRoomServiceServer

	repo        biz.RoomRepo        // 数据访问层
	realtime    biz.RealtimeUseCase // 实时推送服务
	mu          sync.Mutex          // 并发锁
	matchQueue  []string            // 匹配队列
	passwordMap map[string]string   // 私人房间密码→房间ID映射
	log         *log.Helper
}

// NewRoomService 创建房间服务实例
func NewRoomService(repo biz.RoomRepo, realtime biz.RealtimeUseCase, logger log.Logger) *RoomService {
	s := &RoomService{
		repo:        repo,
		realtime:    realtime,
		log:         log.NewHelper(logger),
		matchQueue:  make([]string, 0),
		passwordMap: make(map[string]string),
	}
	// 启动超时清理协程
	go s.cleanupExpiredRooms()
	return s
}

// NewGRPCServer 创建gRPC服务器
func (s *RoomService) NewGRPCServer() *grpc.Server {
	srv := grpc.NewServer()
	v1.RegisterRoomServiceServer(srv, s)
	return srv
}

// HandlePlayerRequest 处理玩家加入/创建房间请求
func (s *RoomService) HandlePlayerRequest(ctx context.Context, req *v1.PlayerRequest) (*v1.RoomResponse, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if req.Password != "" {
		// 私人房间逻辑
		return s.handlePrivateRoom(ctx, req.Username, req.Password)
	}
	// 匹配逻辑
	return s.handleMatching(ctx, req.Username)
}

// SetReadyState 处理准备状态（含卡组提交）
func (s *RoomService) SetReadyState(ctx context.Context, req *v1.SetReadyRequest) (*v1.RoomResponse, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	// 1. 获取房间
	room, err := s.repo.Get(ctx, req.RoomId)
	if err != nil {
		return &v1.RoomResponse{Success: false, Message: "房间不存在"}, nil
	}

	// 2. 检查玩家是否在房间内
	isInRoom := false
	for _, p := range room.Players {
		if p == req.Username {
			isInRoom = true
			break
		}
	}
	if !isInRoom {
		return &v1.RoomResponse{Success: false, Message: "你不在该房间"}, nil
	}

	// 3. 准备时必须提交卡组
	if req.IsReady && req.DeckCode == "" {
		return &v1.RoomResponse{Success: false, Message: "准备时必须提交卡组"}, nil
	}

	// 4. 更新准备状态和卡组
	updated := false
	for _, rs := range room.ReadyStates {
		if rs.Username == req.Username {
			rs.IsReady = req.IsReady
			if req.IsReady {
				rs.DeckCode = req.DeckCode
			}
			updated = true
			break
		}
	}
	if !updated {
		room.ReadyStates = append(room.ReadyStates, &v1.PlayerReadyState{
			Username: req.Username,
			IsReady:  req.IsReady,
			DeckCode: req.DeckCode,
		})
	}

	// 5. 检查是否双方都准备完成
	if s.checkAllReady(room) {
		room.Status = "playing"
		s.realtime.NotifyBattleStart(ctx, room) // 通知对战服务
	} else {
		room.Status = "waiting"
	}

	// 6. 保存并通知
	if err := s.repo.Save(ctx, room); err != nil {
		return &v1.RoomResponse{Success: false, Message: "保存失败"}, err
	}
	s.realtime.BroadcastToRoom(ctx, room.RoomId, &biz.RoomEvent{
		Type: "room_update",
		Room: room,
		Time: time.Now().Unix(),
	})

	return &v1.RoomResponse{Success: true, Room: room}, nil
}

// GetRoomState 获取房间状态
func (s *RoomService) GetRoomState(ctx context.Context, req *v1.GetRoomStateRequest) (*v1.RoomResponse, error) {
	room, err := s.repo.Get(ctx, req.RoomId)
	if err != nil {
		return &v1.RoomResponse{Success: false, Message: "房间不存在"}, nil
	}
	return &v1.RoomResponse{Success: true, Room: room}, nil
}

// ------------------------------
// 私有方法
// ------------------------------

// 处理私人房间
func (s *RoomService) handlePrivateRoom(ctx context.Context, username, password string) (*v1.RoomResponse, error) {
	roomID, exists := s.passwordMap[password]
	var room *v1.Room
	var err error

	if exists {
		room, err = s.repo.Get(ctx, roomID)
		if err != nil || room.Status == "ended" {
			delete(s.passwordMap, password)
			exists = false
		}
	}

	if !exists {
		// 创建新私人房间
		roomID = generateRoomID()
		room = &v1.Room{
			RoomId:      roomID,
			Password:    password,
			Players:     []string{username},
			ReadyStates: []*v1.PlayerReadyState{},
			Status:      "waiting",
			CreateTime:  time.Now().Unix(),
		}
		if err := s.repo.Save(ctx, room); err != nil {
			return &v1.RoomResponse{Success: false, Message: "创建房间失败"}, err
		}
		s.passwordMap[password] = roomID
		s.realtime.BroadcastToRoom(ctx, roomID, &biz.RoomEvent{
			Type: "room_update",
			Room: room,
			Time: time.Now().Unix(),
		})
		return &v1.RoomResponse{Success: true, Room: room}, nil
	}

	// 加入已有房间
	if len(room.Players) >= roomMaxPlayers {
		return &v1.RoomResponse{Success: false, Message: "房间已满"}, nil
	}
	for _, p := range room.Players {
		if p == username {
			return &v1.RoomResponse{Success: true, Room: room}, nil
		}
	}
	room.Players = append(room.Players, username)
	room.CreateTime = time.Now().Unix() // 更新活动时间
	if err := s.repo.Save(ctx, room); err != nil {
		return &v1.RoomResponse{Success: false, Message: "加入失败"}, err
	}
	s.realtime.BroadcastToRoom(ctx, roomID, &biz.RoomEvent{
		Type: "room_update",
		Room: room,
		Time: time.Now().Unix(),
	})
	return &v1.RoomResponse{Success: true, Room: room}, nil
}

// 处理匹配
func (s *RoomService) handleMatching(ctx context.Context, username string) (*v1.RoomResponse, error) {
	for _, p := range s.matchQueue {
		if p == username {
			return &v1.RoomResponse{Success: true, Message: "已在匹配队列"}, nil
		}
	}

	s.matchQueue = append(s.matchQueue, username)
	if len(s.matchQueue) < 2 {
		return &v1.RoomResponse{Success: true, Message: "匹配中..."}, nil
	}

	// 匹配成功，创建房间
	player1, player2 := s.matchQueue[0], s.matchQueue[1]
	s.matchQueue = s.matchQueue[2:]

	roomID := generateRoomID()
	room := &v1.Room{
		RoomId:      roomID,
		Password:    "",
		Players:     []string{player1, player2},
		ReadyStates: []*v1.PlayerReadyState{},
		Status:      "waiting",
		CreateTime:  time.Now().Unix(),
	}
	if err := s.repo.Save(ctx, room); err != nil {
		return &v1.RoomResponse{Success: false, Message: "匹配失败"}, err
	}
	s.realtime.BroadcastToRoom(ctx, roomID, &biz.RoomEvent{
		Type: "room_update",
		Room: room,
		Time: time.Now().Unix(),
	})
	return &v1.RoomResponse{Success: true, Room: room}, nil
}

// 检查所有玩家是否准备完成
func (s *RoomService) checkAllReady(room *v1.Room) bool {
	if len(room.Players) != roomMaxPlayers {
		return false
	}
	for _, p := range room.Players {
		ready := false
		for _, rs := range room.ReadyStates {
			if rs.Username == p && rs.IsReady && rs.DeckCode != "" {
				ready = true
				break
			}
		}
		if !ready {
			return false
		}
	}
	return true
}

// 生成房间ID
func generateRoomID() string {
	b := make([]byte, 8)
	rand.Read(b)
	return "room_" + base64.URLEncoding.EncodeToString(b)[:12]
}

// 清理超时房间
func (s *RoomService) cleanupExpiredRooms() {
	ticker := time.NewTicker(1 * time.Minute)
	defer ticker.Stop()

	for range ticker.C {
		ctx := context.Background()
		expiredTime := time.Now().Unix() - roomTimeout

		expiredRooms, err := s.repo.ListExpired(ctx, expiredTime)
		if err != nil {
			s.log.Warnf("清理超时房间失败: %v", err)
			continue
		}

		for _, room := range expiredRooms {
			if err := s.repo.Delete(ctx, room.RoomId); err != nil {
				s.log.Warnf("删除房间 %s 失败: %v", room.RoomId, err)
				continue
			}
			if room.Password != "" {
				s.mu.Lock()
				delete(s.passwordMap, room.Password)
				s.mu.Unlock()
			}
		}
	}
}
